
Introduction
Microsoft has made it possible for developers to write their own MSN Messenger Activities. These activities are peer to peer since each user is connected through the MSN Messenger network. They can also be client to server at the same time since the activity web page is hosted on your own server. In this article, I will discuss some of the code that is specific to MSN Messenger peer to peer communication as well as a trick or two I used in my own activity (CodeTalk) to overcome some of the restrictions that Microsoft set on these web pages by default, i.e. no ActiveX.
Background
Peer to peer software is where an application allows people on the Internet to communicate with each other (usually in real time) rather than simply communicating with a server. Examples of these types of applications would include chat, file sharing, and white boarding. The MSN Messenger system is a peer to peer application that offers all of these abilities and a lot more. To enable the peer to peer capability to activity authors, MSN Messenger provides some objects that can be called to gather information about the session as well as send/receive data between the peers.
Using the code
MSN Messenger is an Internet Explorer host. As such, it can expose an additional object model to any code (JavaScript) running inside a web page within MSN Messenger. At present, the objects exposed are...
Channel
object - Contains properties, methods, and events for sending and receiving data and for interacting with the MSN� Messenger Chat History window.
Error
object - Contains properties for presenting error information to the user.
FileInfo
object - Contains properties for determining the status of a file transfer.
Messenger
object - Contains methods for launching MSN Messenger client dialog pages.
User
object - Contains properties for ascertaining the user name, e-mail address, and the Microsoft� .NET Passport Unique ID (PUID) of conversants.
Users
collection - The top-level collection of User
objects.
Sending information to a peer is as simple as the following code snippet ...
window.external.Channel.SendData("Some data");
and receiving data requires implementing an event handler like this...
function Channel_OnDataReceived()
{
var myData;
myData=window.external.Channel.Data;
}
That is really the meat of communicating back and forth between peers. You can get as complex as you want in deciding what data to send and how to parse it out on the other end of the connection, then taking an action based on the data received. There are a lot of possibilities here. Games and various kinds of collaboration tools such as shared searching or web browsing come quickly to mind. Microsoft puts a limit on how much data you can send over their network within a specific period of time. Currently, this is 1,664 bytes per packet and a maximum bandwidth of 120 packets per minute (two per second). This may not sound like a lot of data, however, it actually is quite a bit of data, and with a little creativity, you can find ways to enhance your performance using techniques such as caching or look up tables.
In the case of my activity, CodeTalk, I wanted to incorporate the MSN Search Webservice to do on-the-fly searching based on what text the user currently had highlighted. My first attempt used the webservice.htc
behavior. This behavior allows a web page to make asynchronous web service calls over the Internet instead of refreshing a web page to get new results. Now, there are two issues with this. One is that you cannot call Microsoft's webservices from your own web page because you are not allowed to call outside of your own domain (www.YourDomain.com) for security reasons. Second is that this webservice.htc
behavior uses an ActiveX object built into IE, and MSN Messenger, by default, does not give this permission. However, the feature I was building just was not professional without being executed asynchronously. So to get around these issues, I came up with a little trick I show you here.

This trick implements an asynchronous web service call without using any ActiveX and without any real web service! Here are the main points...
- Add an
iFrame
to your web page that is hidden (style=display:none
).
- To make a "web service" call, set the
iFrame
's src
property to point at an ASPX page on your web server. Be sure to pass any method parameters as query string variables (http://domain.com/somepage.aspx?Param1=123&Param2=456).
- The ASPX page calls the MSN Search web service from your server.
- The ASPX page formats the results to return to the
iFrame
and wraps the results in some HTML + JavaScript that fires an event as soon as the iFrame
is finished loading.
- The event that fires upon
iFrame
load takes the contents of the iFrame
and inserts them into the body of a TextArea
in the parent page (since iFrame
s can talk to their parent page, but the parent cannot access the contents of an iFrame
for security reasons).
- Hook to the
OnPropertyChanged
event on the TextArea
in the parent page so that you are notified when the results are returned in the iFrame
.
- Treat the
innerText
property of the TextArea
just like you would the results of a web method call.
This is the HTML + JavaScript that your ASPX page wraps your web method results with before sending it up to the iFrame
:
<HTML><body id=bdy
onload=window.parent.document.all.myTextArea.innerText=bdy.innerHTML;>
YOUR_RESULTS</body></HTML>
When the iFrame
is finished loading the results returned from your ASPX page, the OnLoad
event will fire which makes the innerHTML
of the iFrame
body get put into the innerText
of the TextArea
in your parent page.
Your TextArea
tag in the parent page should look something like this...
<TEXTAREA id="myTextArea"
onpropertychange="AfterWebMethod();" style=display:none;></TEXTAREA>
And your AfterWebMethod()
handler function should check to see if the myTextArea
innerText
length is greater than zero. If it is then your AfterWebMethod()
handler function was called not because of some random property change, but because you have received data back. Also, remember to set the innerText
back to an empty string "" so that you can tell when you get more data back from future web method calls.
function AfterWebMethod()
{
if(myTextArea.innerText.length>0)
{
myTextArea.innerText = "";
}
}
More tips
Another useful trick when making MSN Messenger Activities can be the ability to simulate a mouse pointer on the other user's screen. The technology used to do this is built right into Internet Explorer. This technology is called VML (Vector Markup Language). The following code will show how to create a function that will draw what looks like a mouse pointer on screen at any point you want.
In the OnLoad
event of your web page, use the following code to hook to the body tag's OnMouseMove
event...
document.body.onmousemove = MouseMoved;
Now, you will get alerted each time the mouse moves over the body of your page. If you prefer, you can limit this event to a smaller part of your page such as a DIV
by hooking to the DIV
's OnMouseMove
event instead.
The MouseMoved
JavaScript function looks like this...
function MouseMoved()
{
var mLeft = event.clientX-2;
var mTop = event.clientY-7;
var data = "";
data += mLeft + "|" + mTop;
data += "|" + document.body.clientWidth + "|" +
document.body.clientHeight;
window.external.Channel.SendData(data);
}
Now, you need to capture this data on the peers and parse out the data.
function Channel_OnDataReceived()
{
var myData;
myData=window.external.Channel.Data;
var mouseData = myData.split("|");
MoveMouse(parseInt(mouseData[0]), parseInt(mouseData[1]),
parseInt(mouseData[2]), parseInt(mouseData[3]));
}
Finally, let's take a look at the MoveMouse
function. This code takes the difference between the BODY
on the sender peer with the BODY
of the receiver peer and gets a ratio-ed equivalent on the receiver peer. This may not exactly fit your needs but with some small tweaks, you should get good results.
function MoveMouse(x, y, clientWidth, clientHeight)
{
var xRatio = 0;
var yRatio = 0;
var prevX = 0;
var prevY = 0;
var myWidth = parseInt(document.body.clientWidth);
var myHeight = parseInt(document.body.clientHeight);
xRatio = myWidth/clientWidth;
yRatio = myHeight/clientHeight;
x = (x + ((myWidth)-(clientWidth*xRatio))*xRatio);
y = (y + ((myHeight)-(clientHeight*yRatio))*yRatio);
line1.from = x + "," + y;
line1.to = x + 12 + "," + (y + 11);
prevX = x + 15;
prevY = y + 12;
line2.from = line1.to;
prevX -= 8;
prevY -= 1;
line2.to = prevX + "," + prevY;
line3.from = line2.to;
prevX += 4;
prevY += 8;
line3.to = prevX + "," + prevY;
line4.from = line3.to;
prevX -= 3;
prevY += 1;
line4.to = prevX + "," + prevY;
line5.from = line4.to;
prevX -= 4;
prevY -= 8;
line5.to = prevX + "," + prevY;
line6.from = line5.to;
prevX -= 4;
prevY += 5;
line6.to = prevX + "," + prevY;
line7.from = line6.to;
line7.to = line1.from;
}
Don't forget to include the VML tags for the lines that make up the pointer...
<!---->
<v:line id="line1" from="7500pt,7500pt" to="7500pt,7500pt"></v:line>
<v:line id="line2" from="7500pt,7500pt" to="7500pt,7500pt"></v:line>
<v:line id="line3" from="7500pt,7500pt" to="7500pt,7500pt"></v:line>
<v:line id="line4" from="7500pt,7500pt" to="7500pt,7500pt"></v:line>
<v:line id="line5" from="7500pt,7500pt" to="7500pt,7500pt"></v:line>
<v:line id="line6" from="7500pt,7500pt" to="7500pt,7500pt"></v:line>
<v:line id="line7" from="7500pt,7500pt" to="7500pt,7500pt"></v:line>
And put this at the top of your page instead of a regular <html>
tag...
<html xmlns:v="urn:schemas-microsoft-com:vml"
xmlns:o="urn:schemas-microsoft-com:office:office">
Points of Interest
- Easy peer to peer programming API from Microsoft via their MSN Messenger.
- A simple example of the power of the future of the web SOA (Service Oriented Architecture) where applications call web services which turn around and call other web services (server-to-server) such as the MSN Search web service.
- A technique for performing asynchronous web method calls that do not require using the
webservice.htc
behavior nor the XMLHTTP
object.
- A technique for simulating a mouse pointer on a peer that matches the mouse movements from a source peer.